/*******************************************************************************
 * Descharts library
 * Copyright (c) 2014 Bradipao <bradipao@gmail.com>
 * https://plus.google.com/+SidBradipao
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/

package it.bradipao.lib.descharts;

import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.render.PixelMapHolder;
import ohos.agp.render.Texture;
import ohos.agp.utils.Color;
import ohos.app.Context;
import ohos.media.image.PixelMap;
import ohos.media.image.common.PixelFormat;
import ohos.media.image.common.Size;

import java.util.ArrayList;

/**
 * StyledXyChartView() class represents an XY graph widget.
 *
 * @since 2021-07-27
 */
public class StyledXyChartView extends CartesianView {
    private ArrayList<StyledChartPointSerie> mSeries = new ArrayList<StyledChartPointSerie>();
    private Paint mLinePnt = new Paint();
    private Paint mFillPnt = new Paint();
    private Paint mMarkPnt = new Paint();

    /**
     * Constructor.
     *
     * @param context context
     */
    public StyledXyChartView(Context context) {
        super(context);
        init();
    }

    /**
     * Constructor.
     *
     * @param context context
     * @param attrs attrs
     */
    public StyledXyChartView(Context context, AttrSet attrs) {
        super(context, attrs);
        init();
    }

    /**
     * Draws the plot.
     *
     * @param component component
     * @param cnv cnv
     */
    @Override
    public void onDraw(Component component, Canvas cnv) {

        if ((mBmp == null) || isRedraw) {
            getViewSizes();
            getxyminmax();
            if (isXscaleAuto) {
                calcXgridRange();
            }
            if (isYscaleAuto) {
                calcYgridRange();
            }
            calcxycoefs();
            PixelMap.InitializationOptions options = new PixelMap.InitializationOptions();
            options.pixelFormat = PixelFormat.ARGB_8888;
            options.size = new Size(mWidth, mHeight);
            mBmp = PixelMap.create(options);
            Texture texture = new Texture(mBmp);
            mCnv = new Canvas(texture);

            drawData();
            if (isGridVis) {
                drawGrid();
            }
            if (isXtextVis) {
                drawXlabel();
            }
            if (isYtextVis) {
                drawYlabel();
            }
            if (isBorderVis) {
                drawBorder();
            }
            if (isAxisVis) {
                drawAxis();
            }
            isRedraw = false;
        }
        cnv.drawPixelMapHolder(new PixelMapHolder(mBmp), 0, 0, new Paint());
    }

    /**
     * Clears ArrayList of series.
     */
    public void clearSeries() {
        while (mSeries.size() > 0) {
            mSeries.remove(0);
        }
        isRedraw = true;
        invalidate();
    }

    /**
     * Adds a serie to data holder ArrayList.
     *
     * @param serie serie
     */
    public void addSerie(StyledChartPointSerie serie) {
        mSeries.add(serie);
        isRedraw = true;
        invalidate();
    }

    /**
     * Returns ArrayList of series.
     *
     * @return getSeries
     */
    public ArrayList<StyledChartPointSerie> getSeries() {
        return mSeries;
    }

    /**
     * Sets line visibility.
     *
     * @param index index
     * @param show show
     */
    public void setLineVis(int index, boolean show) {
        mSeries.get(index).setVisible(show);
        isRedraw = true;
        invalidate();
    }

    /**
     * Sets line color and size.
     *
     * @param index index
     * @param size size
     */
    public void setLineStyle(int index, float size) {
        mSeries.get(index).setStyle(size);
        isRedraw = true;
        invalidate();
    }

    /**
     * Sets line color and size and dip.
     *
     * @param index index
     * @param size size
     * @param usedip usedip
     */
    public void setLineStyle(int index, float size, boolean usedip) {
        mSeries.get(index).setStyle(size, usedip);
        isRedraw = true;
        invalidate();
    }

    /**
     * Gets X,Y ranges across all series
     */
    protected void getxyminmax() {
        StyledChartPointSerie serie;
        for (mI = 0; mI < mSeries.size(); mI++) {
            serie = mSeries.get(mI);
            if (mI == 0) {
                mXmin = serie.mXmin;
                mXmax = serie.mXmax;
                mYmin = serie.mYmin;
                mYmax = serie.mYmax;
            } else {
                if (serie.mXmin < mXmin) {
                    mXmin = serie.mXmin;
                }
                if (serie.mXmax > mXmax) {
                    mXmax = serie.mXmax;
                }
                if (serie.mYmin < mYmin) {
                    mYmin = serie.mYmin;
                }
                if (serie.mYmax > mYmax) {
                    mYmax = serie.mYmax;
                }
            }
        }
    }

    /**
     * Draw data from all series
     */
    protected void drawData() {
        float qX = 0, qY = 0, pX = 0, pY = 0;
        boolean pValid;
        StyledChartPoint point;
        mLinePnt.reset();
        mFillPnt.reset();
        mMarkPnt.reset();
        mLinePnt.setStyle(Paint.Style.STROKE_STYLE);
        mFillPnt.setStyle(Paint.Style.FILL_STYLE);
        mMarkPnt.setStyle(Paint.Style.FILL_STYLE);
        mLinePnt.setAntiAlias(true);
        mMarkPnt.setAntiAlias(false);
        mFillPnt.setAntiAlias(false);
        for (StyledChartPointSerie serie : mSeries) {
            if (serie.isVisible()) {
                if (serie.mUseDip) {
                    mLinePnt.setStrokeWidth(dipToPixel(serie.mWidth));
                } else {
                    mLinePnt.setStrokeWidth(serie.mWidth);
                }
                pValid = false;
                for (mI = 0; mI < serie.mPointList.size(); mI++) {
                    point = serie.mPointList.get(mI);
                    pX = point.x;
                    pY = point.y;
                    if (Float.isNaN(pX) || Float.isNaN(pY)) {
                        pValid = false;
                    } else if (!pValid) {
                        if (point.markColor != 0) {
                            mMarkPnt.setColor(new Color(point.markColor));
                            mCnv.drawCircle(mX + (pX - mBx) * mAx, mEy - (pY - mBy) * mAy, point.markSize, mMarkPnt);
                        }
                        pValid = true;
                    } else {
                        if (point.fillColor != 0) {
                            mFillPnt.setColor(new Color(point.fillColor));
                            mPath.reset();
                            mPath.moveTo(mX + (qX - mBx) * mAx, mEy);
                            mPath.lineTo(mX + (qX - mBx) * mAx, mEy - (qY - mBy) * mAy);
                            mPath.lineTo(mX + (pX - mBx) * mAx, mEy - (pY - mBy) * mAy);
                            mPath.lineTo(mX + (pX - mBx) * mAx, mEy);
                            mPath.close();
                            mCnv.drawPath(mPath, mFillPnt);
                        }
                        mLinePnt.setColor(new Color(point.lineColor));
                        mCnv.drawLine(mX + (qX - mBx) * mAx, mEy - (qY - mBy) * mAy, mX + (pX - mBx) * mAx, mEy - (pY - mBy) * mAy, mLinePnt);
                        if (point.markColor != 0) {
                            mMarkPnt.setColor(new Color(point.markColor));
                            mCnv.drawCircle(mX + (pX - mBx) * mAx, mEy - (pY - mBy) * mAy, point.markSize, mMarkPnt);
                        }
                    }
                    qX = pX;
                    qY = pY;
                }
            }
        }
    }
}
